#pragma once

#include <lvgl.h>
#include <array>
#ifdef STM32F407xx
#include<project_config.h>
#endif
#include <Compose/compose.hpp>
#include <bsp/touch/touch.hpp>
#include <bsp/lcd/lcd.hpp>


namespace gui::widgets::main
{
    using namespace gui::compose; // 使用命名空间
}


namespace gui
{
    constexpr uint16_t DISP_HOR_RES = 1024;
    constexpr uint16_t DISP_VER_RES = 600;
    constexpr uint16_t DISP_BUF_SIZE = 10; //20

    /**
     * @brief Render类
     * @tparam UIPolicy UI策略类，这里使用的是LVGL，需要实现屏幕初始化函数screenInit()和事件初始化函数eventInit()
     * @note 渲染类，用于初始化GUI和运行GUI。组件需要定义在gui::widgets::main命名空间
     */
    class control {
        using LCD_Init_Func = void (*)();
        using LCD_Flush_Func = void (*)(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, const uint8_t *color_p);

    public:
        template<typename UIPolicy, bsp::LCDDriver lcdDriver = bsp::NoLCDDriver , bsp::TouchDriver touchDriver =
            bsp::NoTouchDriver>
        static auto init() -> void
        {
            /********初始化LVGL*******/
            lv_init();

            /********初始化显示设备 *******/
            if constexpr (!std::is_same_v<lcdDriver, bsp::NoLCDDriver>) {
                disp_drv_init<lcdDriver>();
            }

            /*****初始化触摸屏******/
            if constexpr (!std::is_same_v<touchDriver, bsp::NoTouchDriver>) {
                touch_drv_init<touchDriver>();
            }


            /*****初始化UI资源*****/
            UIPolicy::screenInit(); // 初始化屏幕
            UIPolicy::eventInit(); // 初始化事件
        }

        // GUI处理函数
        [[gnu::always_inline]] static auto handler() -> void { lv_task_handler(); }

        [[gnu::always_inline]] static auto tick_inc() { lv_tick_inc(1); }

        // 刷新回调,可供DMA回调函数使用
        [[gnu::always_inline]] static auto display_flush_ready() -> void { lv_disp_flush_ready(&disp_drv); }

    private:
        /**
         * @brief 初始化显示驱动
         * @tparam lcdDriver LCD驱动
         * @note 为了让lambda表达式可以不用捕获外部函数，只能使用函数模板。如果使用函数指针来传递就必须要显示捕获
         */
        template<bsp::LCDDriver lcdDriver= bsp::NoLCDDriver>
        static inline auto disp_drv_init() -> void
        {
            // LCD初始化
            lcdDriver::init();

            // 初始化显示设备
            static lv_disp_draw_buf_t draw_buf_dsc{};
            // 缓冲区  双缓冲明显优于单缓冲
            LV_ATTRIBUTE_MEM_ALIGN
            static std::array<lv_color_t, DISP_HOR_RES * DISP_BUF_SIZE> buf_2_1;
            LV_ATTRIBUTE_MEM_ALIGN
            static std::array<lv_color_t, DISP_HOR_RES * DISP_BUF_SIZE> buf_2_2;
            lv_disp_draw_buf_init(&draw_buf_dsc, buf_2_1.data(), buf_2_2.data(), DISP_HOR_RES * DISP_BUF_SIZE);


            lv_disp_drv_init(&disp_drv);
            disp_drv.hor_res = DISP_HOR_RES;
            disp_drv.ver_res = DISP_VER_RES;

            disp_drv.flush_cb = [](_lv_disp_drv_t *, const lv_area_t *area, lv_color_t *color_p) {
                lcdDriver::flush(area->x1, area->y1, area->x2, area->y2, reinterpret_cast<uint8_t *>(color_p));
                // 只有定义了DMA中断回调才不需要这个函数，那么这就要求DMA中断启用时需要设置相关宏定义
#if defined(SIMULATOR) || !defined(ENABLE_DMA_SPI)
                display_flush_ready();
#endif
            };

            disp_drv.draw_buf = &draw_buf_dsc;

            lv_disp_drv_register(&disp_drv);
        }

        template<bsp::TouchDriver touchDriver= bsp::NoTouchDriver>
        [[gnu::always_inline]] static void touch_drv_init()
        {
            // 初始化触摸屏
            touchDriver::init();

            // 初始化触摸屏输入设备
            static lv_indev_drv_t indev_drv{};

            lv_indev_drv_init(&indev_drv);
            indev_drv.type = LV_INDEV_TYPE_POINTER;
            indev_drv.read_cb = [](_lv_indev_drv_t *, lv_indev_data_t *data) {
                if (touchDriver::get_single_point(data->point.x, data->point.y)) {
                    data->state = LV_INDEV_STATE_PRESSED;
                } else {
                    data->state = LV_INDEV_STATE_RELEASED;
                }
            };
            lv_indev_drv_register(&indev_drv);
        }


        static inline lv_disp_drv_t disp_drv{};
    };
}
